﻿//Klimov Ivan
//Saint Petersburg State University
//2013
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using Microsoft.ParallelArrays;
using FPA = Microsoft.ParallelArrays.FloatParallelArray;
using PA = Microsoft.ParallelArrays.ParallelArrays;
namespace AddArrays
{
    class Program
    {

        static void FullDirList(DirectoryInfo dir, List<DirectoryInfo> folders)
        {
            foreach (DirectoryInfo d in dir.GetDirectories())
            {
                folders.Add(d);
            }

        }

        static List<int> readScalesFromFile(string nameFile)
        {
            List<int> listScales = new List<int>();
            StreamReader streamReader = new StreamReader(nameFile);
            string scalesString = "";
            string readingSting;
            while ((readingSting = streamReader.ReadLine()) != null)
            {
                scalesString = scalesString + " " + readingSting;
            }
            streamReader.Close();

            string[] scales = scalesString.Split(' ');
            for (int i = 0; i < scales.Length; i++)
            {
                if (scales[i] != "")
                {
                    listScales.Add(int.Parse(scales[i]));
                }
            }
            return listScales;
        }

        static Int32[] readVecFromFile(string nameFile, uint scale)
        {
            Int32[] vec = new Int32[scale];

            StreamReader streamReader = new StreamReader(nameFile);
            string scalesString = "";
            string readingSting;
            while ((readingSting = streamReader.ReadLine()) != null)
            {
                scalesString = scalesString + " " + readingSting;
            }
            streamReader.Close();

            string[] scales = scalesString.Split(' ');
            int j = 0;
            for (int i = 0; i < scales.Length; i++)
            {
                if (scales[i] != "")
                {
                    vec[j] = Int32.Parse(scales[i]);
                    j++;
                }
            }

            return vec;
        }

        static int getV(DX9Target evalTarget, FPA row, int index)
        {
            bool[] boolCPUArray = new bool[row.GetLength(0)];
            boolCPUArray[index] = true;
            BoolParallelArray boolArray = new BoolParallelArray(boolCPUArray);
            FPA zeroArray = new FPA(0f, row.GetLength(0));
            FPA res = PA.Cond(boolArray, row, zeroArray);
            res = PA.Sum(res);
            float[] resV = evalTarget.ToArray1D(res);
            return (int)resV[0];
        }

        static bool find(DX9Target evalTarget, FPA row, float addElem)
        {
            FPA addingElemArray = new FPA(addElem, row.GetLength(0));
            FPA zero = new FPA(0f, row.GetLength(0));
            FPA one = new FPA(1f, row.GetLength(0));
            FPA resGPU = PA.Sum(PA.Cond(PA.CompareEqual(row, addingElemArray), one, zero));
            float[] res = evalTarget.ToArray1D(resGPU);
            return res[0] > 0;

        }

        static void addingElem(DX9Target evalTarget, FPA row, float addElem, FPA[] StoreRows, int index)
        {
            int[] before = new int[1];
            int[] after = new int[1];
            before[0] = 0;
            after[0] = 1;
            StoreRows[index] = PA.Pad(row, before, after, addElem);
            //  evalTarget.ToArray1D(StoreRows[index]);
        }

        static int addVar(DX9Target evalTarget, FPA row, float addElem, FPA[] StoreRows, Int32[] StoreCount, int index)
        {
            bool isFind = find(evalTarget, row, addElem);
            if (isFind)
            {
                return 1;
            }
            if (StoreCount[index] == 4000)
            {
                return 0;
            }
            addingElem(evalTarget, row, addElem, StoreRows, index);
            StoreCount[index] = StoreCount[index] + 1;

            return 2;
        }

        static void Main(string[] args)
        {
            string resPath = args[1];//@"F:\learn\learn\6_sem\Kursach\MyDevCode\ResTests";
            DirectoryInfo di = new DirectoryInfo(args[0]);//@"F:\learn\learn\6_sem\Kursach\MyDevCode\gpu-sa\SmartGenerator\SmartGenerator\bin\Debug\Tests\SimpleTests");
            List<DirectoryInfo> folders = new List<DirectoryInfo>();
            FullDirList(di, folders);
            foreach (DirectoryInfo folder in folders)
            {
                try
                {
                    if (folder.GetFiles("Fun.txt").Length == 0)
                        break;
                    if (folder.GetFiles("Arg1.txt").Length == 0)
                        break;
                    if (folder.GetFiles("Arg2.txt").Length == 0)
                        break;
                    if (folder.GetFiles("scales.txt").Length == 0)
                        break;

                    startEigenCFA(2, Path.Combine(folder.FullName, "scales.txt"), Path.Combine(folder.FullName, "Fun.txt"),
                        Path.Combine(folder.FullName, "Arg1.txt"), Path.Combine(folder.FullName, "Arg2.txt"),
                        resPath);
                }
                catch (Exception e) { }
            }

        }

        static void startEigenCFA(int numE, string pathToScales, string pathToFun, string pathToArg1, string pathToArg2, string resPath)
        {

            List<int> scales = readScalesFromFile(pathToScales);

            uint scaleCall = (uint)scales[0];
            uint scaleLam = (uint)scales[1];
            uint scaleVar = (uint)scales[2];
            uint scaleExp = scaleLam + scaleVar;
            //uint scaleM = scaleLam / 300 + 1;

            System.Console.WriteLine("call " + scaleCall + " lam " + scaleLam + " numE " + numE);

            Int32[] Fun = readVecFromFile(pathToFun, scaleCall);
            Int32[] Arg1 = readVecFromFile(pathToArg1, scaleCall);
            Int32[] Arg2 = readVecFromFile(pathToArg2, scaleCall);

            int numTimeIter = 5;
            double fullTime = 0;

            Stopwatch stopWatch = new Stopwatch();
            for (int timeIter = 0; timeIter < numTimeIter; timeIter++)
            {
                stopWatch.Start();



                //creating Store
                //Counts rows on CPU
                Int32[] StoreCount = new Int32[scaleExp];
                for (int i = 0; i < scaleExp; i++)
                {
                    StoreCount[i] = i < scaleVar ? 1 : 2;
                }
                //rows on GPU
                FPA[] StoreRows = new FPA[scaleExp];
                for (int i = 0; i < scaleExp; i++)
                {
                    if (i < scaleVar)
                    {
                        float[] row = new float[1];
                        row[0] = -2;
                        StoreRows[i] = new FPA(row);
                    }
                    else
                    {
                        float[] row = new float[2];
                        row[0] = -2;
                        row[1] = i - scaleVar + 1;
                        StoreRows[i] = new FPA(row);
                    }
                }

                // [1] 
                DX9Target evalTarget = new DX9Target();


                //          printStore(evalTarget, StoreRows, (int)scaleExp, StoreCount);
                int iter = 1;
                bool changed = true;
                while (changed)
                {
                    //   System.Console.WriteLine("Iter = " + iter);
                    iter++;
                    changed = false;
                    int error = 0;
                    for (int i = 0; i < scaleCall; i++)
                    {
                        //                  Console.WriteLine("Call = " + i);
                        int indexL = Fun[i];
                        int indexL1 = Arg1[i];
                        int indexL2 = Arg2[i];

                        for (int j = 1; j < StoreCount[indexL]; j++)
                        {
                            int v1 = getV(evalTarget, StoreRows[indexL], j) - 1;
                            int v2 = v1 + (int)scaleLam;

                            for (int k = 1; k < StoreCount[indexL1]; k++)
                            {
                                error = addVar(evalTarget, StoreRows[v1], getV(evalTarget, StoreRows[indexL1], k), StoreRows, StoreCount, v1);

                                if (error == 0)
                                {
                                    Console.WriteLine("mem End");
                                    return;
                                }
                                else if (error == 2)
                                {
                                    changed = true;
                                }
                            }

                            for (int k = 1; k < StoreCount[indexL2]; k++)
                            {
                                error = addVar(evalTarget, StoreRows[v2], getV(evalTarget, StoreRows[indexL2], k), StoreRows, StoreCount, v2);

                                if (error == 0)
                                {
                                    Console.WriteLine("mem End");
                                    return;
                                }
                                else if (error == 2)
                                {
                                    changed = true;
                                }
                            }

                        }
                        // printStore(evalTarget, StoreRows, (int)scaleExp, StoreCount);

                    }
                }

                stopWatch.Stop();
                fullTime = fullTime + stopWatch.ElapsedMilliseconds;
            }
            // Format and display the TimeSpan value.
            System.Console.WriteLine(fullTime / numTimeIter);

            writeTimeToFile(resPath, "Accelerator.txt", fullTime / numTimeIter, (int)(scaleLam * 3 + scaleCall), numE);
        }

        static void printStore(DX9Target evalTarget, FPA[] StoreRows, int scaleExp, Int32[] StoreCount)
        {
            float[] stackedArray;
            for (int i = 0; i < scaleExp; i++)
            {
                stackedArray = evalTarget.ToArray1D(StoreRows[i]);
                Console.Write(i + ". " + StoreCount[i]);
                for (int j = 1; j < StoreCount[i]; j++)
                {
                    Console.Write(" " + stackedArray[j].ToString());
                }
                Console.WriteLine();
            }
            Console.WriteLine();
            Console.Read();
        }

        static void writeTimeToFile(string pathFolder, string nameFile, double time, int numTerm, int numE)
        {
            string pathFile = Path.Combine(pathFolder, nameFile);
            System.IO.File.AppendAllLines(pathFile, new string[] { numTerm + " " + (time/1000).ToString().Replace(",",".") });//+ " " + numE});
            //FileStream fileStream = new FileStream(pathFile, FileMode.Append);
            //using (StreamWriter sw = new StreamWriter(fileStream))
            //{
            //    fileStream.Seek(0, SeekOrigin.End);

            //    sw.WriteLine(numTerm + " " + time + " " + numE);

            //    sw.Flush();

            //    sw.Close();
            //}
        }


    }
}